package db

import (
	"time"

	"example.com/m/util"
	"github.com/kataras/iris/v12"
	"github.com/pkg/errors"
	"github.com/qf-tech/qftech-base/pkg/log"
	"github.com/uptrace/bun"
)

var Tasks *tasks

type tasks struct {
}

type TasksUpdateOptions struct {
	UID       string
	Status    string
	MetaData  map[string]interface{}
	DeletedAt time.Time
}

type TasksModel struct {
	bun.BaseModel `bun:"table:tasks"`

	ID        int64                  `bun:"id,pk,autoincrement"`
	UID       string                 `bun:"uid"`
	Status    string                 `bun:"status"`
	MetaData  map[string]interface{} `bun:"metadata"`
	CreatedAt time.Time              `bun:"created_at"`
	UpdatedAt time.Time              `bun:"updated_at"`
	DeletedAt time.Time              `bun:"deleted_at,soft_delete"`
}

func (t *tasks) Create(ctx iris.Context, opts TasksUpdateOptions) error {
	tasksModel := TasksModel{
		UID:       opts.UID,
		Status:    opts.Status,
		MetaData:  opts.MetaData,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	q := BunDb.NewInsert().Model(&tasksModel)

	_, err := q.Exec(ctx.Request().Context())
	if err != nil {
		return errors.Wrap(err, "create task")
	}
	return nil
}

func (t *tasks) Update(ctx iris.Context, opts TasksUpdateOptions) error {
	value := make(map[string]interface{})
	if opts.Status != "" {
		value["status"] = opts.Status
	}

	if len(opts.MetaData) > 0 {
		value["metadata"] = opts.MetaData
	}

	value["updated_at"] = time.Now()

	_, err := BunDb.NewUpdate().
		Model(&value).
		TableExpr("tasks").
		Where("uid = ?", opts.UID).
		Exec(ctx.Request().Context())

	if err != nil {
		return errors.Wrap(err, "update task")
	}

	return nil
}

type TasksResult struct {
	Total     *int
	TasksInfo []*TasksModel
}

func (t *tasks) Get(ctx iris.Context, uid string) (*TasksResult, error) {
	result := TasksResult{}

	q := BunDb.NewSelect().
		Model(&result.TasksInfo).
		ColumnExpr("*").
		Where("uid = ?", uid)

	err := q.Scan(ctx.Request().Context())
	if err != nil {
		log.GetLogger().GetCtx(ctx.Request().Context()).Sugar().Errorf("get task info err: %v", err)
		return nil, err
	}

	return &result, nil
}

type TaskGetOptions struct {
	TaskID         string
	TaskName       string
	Page           int
	PageSize       int
	IncludeDeleted bool
	Sorts          []string
}

func (t *tasks) GetV2(ctx iris.Context, opts TaskGetOptions) (*TasksResult, *int, error) {
	log := log.GetLogger().GetCtx(ctx.Request().Context()).Sugar()
	var result TasksResult

	whereFun := func(q *bun.SelectQuery) *bun.SelectQuery {
		if opts.TaskName != "" {
			q.Where("task_name LIKE ?", "%"+opts.TaskName+"%")
		}

		q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
			if opts.TaskID != "" {
				q.Where("uid = ?", opts.TaskID)
			}
			return q
		})

		q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
			if opts.IncludeDeleted {
				q.WhereAllWithDeleted()
			}
			return q
		})
		return q
	}

	// 若无排序规则，则按 created_at 降序
	if len(opts.Sorts) == 0 {
		opts.Sorts = append(opts.Sorts, "created_at DESC")
	}

	q := BunDb.NewSelect().
		Model(&result.TasksInfo).
		ColumnExpr("id, uid, status, metadata, created_at, updated_at").
		WhereGroup(" AND ", whereFun).
		Order(opts.Sorts...).
		Limit(opts.PageSize + 1).
		Offset((opts.Page - 1) * opts.PageSize)

	countQ := BunDb.NewSelect().
		Model((*TasksModel)(nil)).
		ColumnExpr("id, uid, status, metadata, created_at, updated_at").
		WhereGroup(" AND ", whereFun)

	err := q.Scan(ctx.Request().Context())
	if err != nil {
		log.Errorf("get task info err: %v, sql: %s", err, q.DB().String())
		return nil, nil, err
	}

	total, err := countQ.Count(ctx.Request().Context())
	if err != nil {
		log.Errorf("get task info count err: %v, sql: %s", err, countQ.DB().String())
		return nil, nil, err
	}

	var nextPage *int
	if len(result.TasksInfo) > opts.PageSize {
		result.TasksInfo = result.TasksInfo[0 : len(result.TasksInfo)-1]
		nextPage = util.IntPtr(opts.Page + 1)
	}

	result.Total = &total
	return &result, nextPage, nil
}
